/**
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 *   
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 */
package org.jiopi.ibean.initializer;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jiopi.framework.core.Config;
import org.jiopi.framework.core.initializer.Initializer;
import org.jiopi.framework.exception.CentralConsoleInitializeException;
import org.jiopi.ibean.loader.IBeanLoaderConstants;
import org.jiopi.ibean.loader.log.LoaderLogUtil;
import org.jiopi.ibean.setup.Setup;
import org.jiopi.ibean.share.LocalJarPriorClassLoader;
import org.jiopi.ibean.share.ShareConstants;
import org.jiopi.ibean.share.ShareUtil.ClassUtil;
import org.jiopi.ibean.share.ShareUtil.FileUtil;
import org.jiopi.ibean.share.ShareUtil.ResourceUtil;


/**
 * 
 * iBean框架初始化程序
 * 
 * @since 2010.4.11
 * 
 */
public class IBeanInitializer implements Initializer {
	
	private static volatile boolean initialized = false;
	
	private static volatile ClassLoader bootstrapClassLoader = null;

	public void initialize() {
		iBeanInitialize();
	}
	
	public static ClassLoader getBootstrapClassLoader(){
		return bootstrapClassLoader;
	}
	
	/**
	 * 初始化jiopi运行所需的相关程序
	 * 
	 */
	public synchronized static void iBeanInitialize(){
		if(!initialized){
			long begin = System.currentTimeMillis();
			//初始化Loader日志程序
			//日志目录使用iBean载入ClassLoader中的配置,如果没有配置文件,则只使用Console输出
			boolean logFileSeted = false;
			Properties[] jiopiProperties = ResourceUtil.getJIOPICascadingConfig();
			String workDir = ResourceUtil.getPropertyValue(ShareConstants.IBEAN_WORK_DIR, jiopiProperties,ShareConstants.IBEAN_WORK_DIR_DEFAULT, false);
			workDir = FileUtil.getPathWithSystemProperty(workDir);
			if(workDir!=null){
				workDir = FileUtil.getPathWithSystemProperty(workDir);
				File work = FileUtil.confirmDir(workDir,true);
				if(work!=null && work.canWrite()){
					LoaderLogUtil.initialize(work.getAbsolutePath(), ResourceUtil.getPropertyValue(IBeanLoaderConstants.LOADER_LOG_LEVEL,jiopiProperties, IBeanLoaderConstants.LOADER_LOG_LEVEL_DEFAULT,false));
					logFileSeted = true;
				}
			}
			Logger loaderLogger = LoaderLogUtil.getLoaderLogger();
			if(!logFileSeted){
				loaderLogger.config("Loader log will print to console only.");
			}
			
			ClassLoader contextClassLoader = ClassUtil.getClassLoaderByClass(IBeanInitializer.class);
			
			//日志初始化完毕,打印iBean Loader信息
			loaderLogger.info(IBeanLoaderConstants.COPY_RIGHT);
			
			//1.查找程序安装目录(两级查找,当前类的装载路径,和ContextClassLoader的装载路径)
			String iBeanProgramPath = ResourceUtil.getIBeanProgramDir();
			
			loaderLogger.config("Use iBean program dir : "+iBeanProgramPath);
			String[] vers = FileUtil.getVersionsDirName(iBeanProgramPath);
			
			//检查新版本,如果自动更新打开,如果存在新版本,重新下载setup.jar进行安装
			boolean reInstall = false;
			String autoUpdate = Config.getJiopiProperties().getProperty(ShareConstants.IBEAN_AUTOUPDATE_INTERVAL, ShareConstants.IBEAN_AUTOUPDATE_INTERVAL_DEFAULT);
			if(vers!=null&&vers.length>0&&"0".equals(autoUpdate)){
				loaderLogger.info("checking for kernel update...");
				String checkFile = FileUtil.joinPath(Config.getConfig(ShareConstants.IBEAN_HOME),IBeanLoaderConstants.SETUP_CONFIG_FILE);
				URL checkURL = FileUtil.toURL(checkFile);
				try {
					Properties updateConfig = ResourceUtil.getPropertiesFormURL(checkURL);
					String newVersion = updateConfig.getProperty(IBeanLoaderConstants.IBEAN_VERSION);
					loaderLogger.info("the lasted version is "+newVersion +" and your installed version is "+vers[0]);
					if(!vers[0].startsWith(newVersion)){
						reInstall = true;
						loaderLogger.info("iBean will update to lasted version.");
					}
				} catch (IOException e) {
					LoaderLogUtil.logExceptionTrace(loaderLogger, Level.WARNING, e, "auto update for kernel is failure!");
				}
			}
			
			//2.如果已经安装程序,跳过该步骤,直接到 3.
			if(reInstall||vers == null||vers.length<1){//没有任何版本
				vers = install(iBeanProgramPath);
			}
			if(vers == null || vers.length<1){//安装程序失败
				loaderLogger.severe("iBean installation is not successful!");
				throw new CentralConsoleInitializeException("iBean installation is not successful!");
			}
			
			String ibeanProgramVersionPath = FileUtil.joinPath(iBeanProgramPath,vers[0]);
			loaderLogger.info("Using iBean program at "+ibeanProgramVersionPath);
			
			//3.装载bootstrap.jar 运行 initialize函数初始化,非必须行为,允许初始化失败
			
			bootstrapClassLoader = contextClassLoader;
			String toolsJar = null;
			if(!ShareConstants.debugMode){
				String bootstrapPath = FileUtil.joinPath(ibeanProgramVersionPath,ShareConstants.BOOTSTRAP_DIR_PATH);
				File bootstrapDir = new File(bootstrapPath);
				URL[] bootstrapJars = FileUtil.toURL(bootstrapDir.listFiles());
				//装载tools.jar
					String javaHome = System.getenv("JAVA_HOME");
					String jreHome = System.getProperty("java.home");
					if(javaHome!=null){
						File javaHomeDir = new File(javaHome);
						File toolsJarFile = new File(javaHomeDir,"lib/tools.jar");
						if(toolsJarFile.isFile()){
							toolsJar = toolsJarFile.getAbsolutePath();
						}
					}else if(jreHome!=null){
						File javaHomeDir = new File(jreHome);
						javaHomeDir = javaHomeDir.getParentFile();
						File toolsJarFile = new File(javaHomeDir,"lib/tools.jar");
						if(toolsJarFile.isFile()){
							toolsJar = toolsJarFile.getAbsolutePath();
						}
					}
				if(toolsJar!=null){
					URL[] newBootstrapJars = new URL[bootstrapJars.length+1];
					for(int i=0;i<bootstrapJars.length;i++){
						newBootstrapJars[i]=bootstrapJars[i];
					}
					bootstrapJars = newBootstrapJars;
					bootstrapJars[bootstrapJars.length-1] = FileUtil.toURL(toolsJar);
				}
				bootstrapClassLoader = new LocalJarPriorClassLoader(bootstrapJars,contextClassLoader);
			}
				try{
					Properties bootstrapProperties = ResourceUtil.getPropertiesFromClassPath(bootstrapClassLoader, IBeanLoaderConstants.BOOTSTRAP_CONFIG_FILE,false);
					String bootstrapInitClassName = bootstrapProperties.getProperty(IBeanLoaderConstants.IBEAN_BOOTSTRAP_INITIALIZER_CLASS);
					Class<?> bootstrapInitClass=null;
					try {
						bootstrapInitClass = bootstrapClassLoader.loadClass(bootstrapInitClassName);
					} catch (ClassNotFoundException e) {
						LoaderLogUtil.logExceptionTrace(loaderLogger, Level.WARNING, e, "iBean initialization ERROR!");
					}
					if(bootstrapInitClass!=null){
						Initializer initializer=null;
						try {
							initializer = (Initializer)bootstrapInitClass.newInstance();
							initializer.initialize();
						} catch (InstantiationException e) {
							LoaderLogUtil.logExceptionTrace(loaderLogger, Level.WARNING, e, "iBean initialization ERROR!");
						} catch (IllegalAccessException e) {
							LoaderLogUtil.logExceptionTrace(loaderLogger, Level.WARNING, e, "iBean initialization ERROR!");
							throw new CentralConsoleInitializeException(e);
						}
					}
				}catch(Exception e){
					LoaderLogUtil.logExceptionTrace(loaderLogger, Level.WARNING, e, "");
				}
			
			
			initialized = true;//标记已经初始化
			
			//初始化完成
			long used = System.currentTimeMillis() - begin;
			loaderLogger.info("Initialization processed in "+used+" ms");
		}
	}
	
	private static String[] install(String iBeanProgramPath){
		Logger loaderLogger = LoaderLogUtil.getLoaderLogger();
		ClassLoader contextClassLoader = ClassUtil.getClassLoaderByClass(IBeanInitializer.class);
		//2.a.下载setup.jar
		//获取iBean home
		String iBeanHomePath = Config.getConfig(ShareConstants.IBEAN_HOME);
		String setupJarPATH = FileUtil.joinPath(iBeanHomePath,IBeanLoaderConstants.SETUP_JAR);
		URL setupJarURL = FileUtil.toURL(setupJarPATH);
		String setupJarFilePath = FileUtil.joinPath(iBeanProgramPath,IBeanLoaderConstants.SETUP_JAR);
		File setupJarFile = new File(setupJarFilePath);
		try {
			ResourceUtil.copyContent(setupJarURL, setupJarFile, false);
			if(!setupJarFile.isFile()){
				loaderLogger.severe("Download setup program from "+setupJarPATH+" to "+setupJarFilePath+" ERROR!");
				throw new RuntimeException("iBean initialization ERROR!");
			}
			loaderLogger.info("Download setup program from "+setupJarPATH+" OK!");
		} catch (Exception e) {
			loaderLogger.severe("Download setup program from "+setupJarPATH+" ERROR!");
			throw new RuntimeException("iBean initialization ERROR!",e);
		}
		
		//2.b.运行setup.jar,将下载安装所需文件到其版本目录,并标记最新可用的版本所在目录
		try{
			URL setupJarLocalURL= FileUtil.toURL(setupJarFilePath);
			ClassLoader setupJarClassLoader = contextClassLoader;
			if(!ShareConstants.debugMode){
				setupJarClassLoader = new LocalJarPriorClassLoader(new URL[]{setupJarLocalURL},contextClassLoader);
			}
			Properties setupProperties = ResourceUtil.getPropertiesFromClassPath(setupJarClassLoader, IBeanLoaderConstants.SETUP_CONFIG_FILE,false);
			String setupClassName = setupProperties.getProperty(IBeanLoaderConstants.IBEAN_SETUP_CLASS);
			Class<?> setupClass=null;
			try {
				setupClass = setupJarClassLoader.loadClass(setupClassName);
			} catch (ClassNotFoundException e) {
				LoaderLogUtil.logExceptionTrace(loaderLogger, Level.SEVERE, e, "iBean initialization ERROR!");
				throw new CentralConsoleInitializeException("iBean initialization ERROR!",e);
			}
			Setup setup=null;
			try {
				setup = (Setup)setupClass.newInstance();
			} catch (InstantiationException e) {
				LoaderLogUtil.logExceptionTrace(loaderLogger, Level.SEVERE, e, "iBean initialization ERROR!");
				throw new CentralConsoleInitializeException(e);
			} catch (IllegalAccessException e) {
				LoaderLogUtil.logExceptionTrace(loaderLogger, Level.SEVERE, e, "iBean initialization ERROR!");
				throw new CentralConsoleInitializeException(e);
			}
			setup.setup();//执行安装过程
		}finally{
			setupJarFile.deleteOnExit();
		}
		return FileUtil.getVersionsDirName(iBeanProgramPath);
	}

}
